﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using COFE.Core.Utils;

namespace COFE.Core.Actions
{
    public abstract class WorkBase : IWork, IWorkProgress
    {
        #region Constructor

        protected WorkBase(Guid id, string param = null)
        {            
            ID = id;
            Type thisType = this.GetType();
            Name = WorkUtils.FormatWorkName(thisType.Namespace,
                thisType.Name, param);
            //Console.WriteLine(Name);
            Description = "Unspecified";
            ConstructTimeUtc = DateTime.UtcNow;
            WorkStatus = StatusType.Initalized;
        }

        protected WorkBase(Guid id, string libraryAlias, string workName,
            string parameter = null)            
        {
            ID = id;
            Name = WorkUtils.FormatWorkName(libraryAlias, workName, parameter);
            Description = "Unspecified";
            ConstructTimeUtc = DateTime.UtcNow;
            WorkStatus = StatusType.Initalized;
        }

        #endregion

        #region Methods

        protected abstract void DoWork();        

        public void CheckPause()
        {
            _pauseTrigger.WaitOne();
        }

        public void Pause()
        {
            //Logger.Log(this, logType.paused, "");
            _pauseTrigger.Reset();
            _paused = true;
            OnWorkPaused(new WorkEventArgs(this, EventType.Paused));
        }

        public void Resume()
        {
            //Logger.Log(this, logType.resumed, "");
            _pauseTrigger.Set();
            _paused = false;
            OnWorkResumed(new WorkEventArgs(this, EventType.Resumed));
        }


        protected void doWork()
        {
            try
            {
                DoWork();
                OnWorkFinished(WorkFinishedEventArgs.SuccessArgs(this));
            }
            catch (Exception ex)
            {
                OnWorkFinished(WorkFinishedEventArgs.FailedArgs(this, ex));
            }
            finally
            {
                _workLock.Set();
            }
        }

        public void Start(bool threaded)
        {
            OnWorkStarted(new WorkEventArgs(this, EventType.Started));

            _workLock.Reset();
            if (threaded)
            {
                _worker = new Thread(new ThreadStart(doWork));
                _worker.Start();
            }
            else doWork();

        }


        public void Abort()
        {
            _aborted = true;

            if (Paused)
                Resume();

            OnWorkFinished(WorkFinishedEventArgs.AbortArgs(this));

        }

        ///// <summary>
        ///// Update the list of construct parameters, 
        ///// which is then be serialized to a string using StringUtils.DicToParamString()
        ///// * This is not serializable.
        ///// </summary>
        ///// <param name="paramDic"></param>
        //protected virtual void GetObjectData(Dictionary<string, string> paramDic)
        //{
        //    paramDic.Add("id", this.ID.ToString());
        //    paramDic.Add("desc", this.Description);
        //}

        //protected virtual void SetObjectData(Dictionary<string, string> paramDic)
        //{
        //    this.ID = new Guid(paramDic["id"]);
        //    this.Description = paramDic["desc"];
        //}

        #region Event Methods
        public virtual void OnWorkStarted(WorkEventArgs e)
        {
            if (WorkStart != null)
                WorkStart(this, e);

            StartTimeUtc = DateTime.UtcNow;
            WorkStatus = StatusType.Working;
            LastMessage = null;
            LastProgress = null;
            Result = null;
        }

        public virtual void OnWorkFinished(WorkFinishedEventArgs e)
        {
            if (WorkFinished != null)
                WorkFinished(this, e);
            Result = e;
            WorkStatus = e.Success ? StatusType.Completed : StatusType.Error;
            this.EndTimeUtc = DateTime.UtcNow;
        }

        public virtual void OnWorkPaused(WorkEventArgs e)
        {
            WorkStatus = StatusType.Paused;
            if (WorkPaused != null)
                WorkPaused(this, e);
        }

        public virtual void OnWorkResumed(WorkEventArgs e)
        {
            WorkStatus = StatusType.Working;
            if (WorkResumed != null)
                WorkResumed(this, e);
        }

        public virtual bool OnWorkMessage(WorkMessageEventArgs e)
        {
            if (WorkMessage != null)
                WorkMessage(this, e);
            LastMessage = e;

            if (this.Aborted)
                e.Cancel = true;

            return !e.Cancel;
        }

        public virtual bool OnWorkProgress(WorkProgressEventArgs e)
        {
            if (WorkProgress != null)
                WorkProgress(this, e);
            LastProgress = e;

            if (this.Aborted)
                e.Cancel = true;

            return !e.Cancel;
        }
        #endregion

        #endregion

        #region Data

        Thread _worker;
        List<string> _messages = new List<string>();
        bool _paused = false, _aborted = false;
        ManualResetEvent _pauseTrigger = new ManualResetEvent(true);
        ManualResetEvent _workLock = new ManualResetEvent(true);

        #endregion

        #region Public Properties

        public Guid ID { get; private set; }
        public string Name { get; private set; }
        public IWork Work { get { return this; } }
        public string Description { get; protected set; }

        //public string ConstructParameters
        //{
        //    get
        //    {
        //        var dic = new Dictionary<string, string>(); 
        //        GetObjectData(dic);
        //        return StringUtils.DicToParamString(dic);
        //    }
        //}

        public DateTime ConstructTimeUtc { get; private set; }
        public DateTime StartTimeUtc { get; private set; }
        public DateTime EndTimeUtc { get; private set; }

        public ManualResetEvent WorkLock { get { return _workLock; } }
        public string[] MessageLog { get { return _messages.ToArray(); } }
        public WorkMessageEventArgs LastMessage { get; protected set; }
        public WorkProgressEventArgs LastProgress { get; protected set; }
        public WorkFinishedEventArgs Result { get; protected set; }
        public StatusType WorkStatus { get; protected set; }
        public int PercentageCompleted { get; private set; }

        public bool Paused
        {
            get { return _paused; }
            set { if (value) Pause(); else Resume(); }
        }
        public bool Aborted { get { return _aborted; } }

        public event EventHandler<WorkProgressEventArgs> WorkProgress;
        public event EventHandler<WorkMessageEventArgs> WorkMessage;
        public event EventHandler<WorkEventArgs> WorkStart;
        public event EventHandler<WorkFinishedEventArgs> WorkFinished;
        public event EventHandler<WorkEventArgs> WorkPaused;
        public event EventHandler<WorkEventArgs> WorkResumed;

        #endregion

    }
}
